package com.flying.flow.engine;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.StringUtils;
import com.flying.common.util.Utils;
import com.flying.flow.model.Flow;
import com.flying.flow.model.FlowDefinition;
import com.flying.flow.model.FlowInstance;
import com.flying.flow.model.FlowInstance.FlowInstanceStatus;
import com.flying.flow.model.Node;
import com.flying.flow.model.Node.EmergencyLevel;
import com.flying.flow.model.Node.FinishCondition;
import com.flying.flow.model.Node.NodeType;
import com.flying.flow.model.Node.PriorityLevel;
import com.flying.flow.model.Node.RouteMode;
import com.flying.flow.model.Node.TaskMode;
import com.flying.flow.model.Node.TaskStartType;
import com.flying.flow.model.NodeInstance;
import com.flying.flow.model.NodeInstance.NodeInstanceStatus;
import com.flying.flow.model.NodeTransition;
import com.flying.flow.model.Task;
import com.flying.flow.model.Task.DealResult;
import com.flying.flow.model.Task.TaskStatus;
import com.flying.flow.participant.Participant;
import com.flying.flow.participant.ParticipantProviderFactory;
import com.flying.flow.participant.ParticipantService;
import com.flying.flow.service.FlowDefinitionService;
import com.flying.flow.service.FlowInstanceService;
import com.flying.flow.service.FlowService;
import com.flying.flow.service.NodeInstanceService;
import com.flying.flow.service.NodeService;
import com.flying.flow.service.NodeTransitionService;
import com.flying.flow.service.TaskParticipantService;
import com.flying.flow.service.TaskService;
import com.flying.flow.util.FlowUtils;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.context.ServiceContext;
import com.flying.framework.data.Data;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.security.Principal;

@Service(value="FlowEngine", desc="流程引擎")
public class FlowEngine {
	private FlowService fs;
	private NodeService ns;
	private FlowDefinitionService fds;
	private FlowInstanceService fis;
	private NodeInstanceService nis;
	private NodeTransitionService nts;
	private TaskService ts;
	private TaskParticipantService tps;
	private ParticipantService ps;
	private ParticipantProviderFactory ppf;
	
	public void setFlowService(FlowService s) {
		this.fs = s;
	}
	public void setNodeService(NodeService s) {
		this.ns = s;
	}
	public void setFlowDefinitionService(FlowDefinitionService s) {
		this.fds = s;
	}
	public void setFlowInstanceService(FlowInstanceService s) {
		this.fis = s;
	}
	public void setNodeInstanceService(NodeInstanceService s) {
		this.nis = s;
	}
	public void setNodeTransitionService(NodeTransitionService s) {
		this.nts = s;
	}
	public void setTaskService(TaskService s) {
		this.ts = s;
	}
	public void setTaskParticipantService(TaskParticipantService s) {
		this.tps = s;
	}
	public void setParticipantService(ParticipantService s) {
		this.ps = s;
	}
	public void setParticipantProviderFactory(ParticipantProviderFactory s) {
		this.ppf = s;
	}
	//启动流程
	/**
	 * 开始一个流程实例
	 * 1、创建Start节点实例
	 * 2、创建Start节点任务（自动任务）
	 * 3、触发Start节点onEnter事件，相当于流程启动事件
	 */
	@Transaction
	public FlowInstance startFlow(
			@Param(value=FlowInstance.ORG_CODE, required=false,	desc="机构代码") String org_code,
			@Param(value=FlowInstance.BUSINESS_TYPE, required=true,	desc="业务类型") String business_type,
			@Param(value=FlowInstance.BUSINESS_CODE, required=true,	desc="业务代码") String business_code,
			@Param(value=FlowInstance.NAME, required=false,	desc="名称") String name,
			@Param(value=FlowInstance.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=FlowInstance.PRIORITY_LEVEL, required=false,enumClass=PriorityLevel.class,	desc="优先级别") Integer priority_level,
			@Param(value=FlowInstance.EMERGENCY_LEVEL, required=false, enumClass=EmergencyLevel.class,	desc="紧急程度") Integer emergency_level) throws Exception {
		Flow f = null;
		try {
			f = this.fs.findByOrgBusinessType(org_code, business_type);
		} catch (Exception e) {
			f = this.fs.findByBusinessType(business_type);
		}
		FlowDefinition fd = this.fds.findById(f.getCurrentFlowDef());
		List<Node> nodes = fd.getNodes();
		//1、创建流程实例
		FlowInstance fi = this.fis.create(fd.getFlowDefId(), f.getFlowId(), org_code, business_type, business_code, 
				name, remarks, priority_level == null?PriorityLevel.LOW.value():priority_level, emergency_level==null?EmergencyLevel.NORMAL.value():emergency_level);
		//2、创建Start节点实例
		Node startNode = nodes.stream().filter(x -> x.getNodeType() == NodeType.START.value()).findFirst().get();
		NodeInstance ni = this.nis.createByNode(null, fi.getFlowInstanceId(), startNode);
		this.fireEnterNodeEvent(startNode, ni, startNode.getEventOnEnter());
		this.nis.finish(ni.getNodeInstanceId(), NodeInstanceStatus.SUCCESS.value(), "SYSTEM", null, null, 0L, null, null);
		//3、搜索开始环节后的环节，并创建任务
		List<Node> nextNodes = this.findNextNodes(fi, startNode, null);
		this.createNextTasks(fi, nextNodes, null, null, org_code, business_type, business_code);
		//
		return fi;
	}
	
	//无条件分配任务
	@MethodInfo("分配任务")
	public ActionResult assign(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.EMERGENCY_LEVEL, required=false, enumClass=EmergencyLevel.class,	desc="紧急程度") Integer emergency_level,
			@Param(value=Task.PRIORITY_LEVEL, required=false,enumClass=PriorityLevel.class,	desc="优先级别") Integer priority_level,
			@Param(value=Task.PLAN_START, required=false,	desc="计划开始时间") Date plan_start,
			@Param(value=Task.PLAN_END, required=false,	desc="计划结束时间") Date plan_end,
			@Param(value=Task.PARTICIPANT_ID, required=true,	desc="执行人") String participant_id) throws Exception {
		Task t = this.ts.findById(task_id);
		if(t.getStatus() != TaskStatus.ASSIGNING.value()) {
			throw new AppException("0101","待分配状态任务才能分配");
		}
		Node node = this.ns.findById(t.getNodeId());
		if(node.getNodeType() == NodeType.AUTO.value()) {
			throw new AppException("0101",node.getName() + "不是人工处理节点，不能分配！");
		}
		ActionResult r = this.ts.assign(task_id, emergency_level, priority_level, plan_start, plan_end, participant_id, null, null, null);
		if(node.getTaskStartType() == TaskStartType.AUTO.value()) {
			this.start(t.getTaskId(), participant_id);
		}
		return r;
	}
	
	//无条件分配任务
	@MethodInfo("再分配任务")
	public ActionResult reassign(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.EMERGENCY_LEVEL, required=false, enumClass=EmergencyLevel.class,	desc="紧急程度") Integer emergency_level,
			@Param(value=Task.PRIORITY_LEVEL, required=false,enumClass=PriorityLevel.class,	desc="优先级别") Integer priority_level,
			@Param(value=Task.PLAN_START, required=false,	desc="计划开始时间") Date plan_start,
			@Param(value=Task.PLAN_END, required=false,	desc="计划结束时间") Date plan_end,
			@Param(value=Task.PARTICIPANT_ID, required=true,	desc="执行人") String participant_id) throws Exception {
		Task t = this.ts.findById(task_id);
		if(t.getStatus() != TaskStatus.ASSIGNED.value()) {
			throw new AppException("0101","已分配状态任务才能再分配");
		}
		Node node = this.ns.findById(t.getNodeId());
		if(node.getNodeType() == NodeType.START.value() || 
				node.getNodeType() == NodeType.END.value() || 
				node.getNodeType() == NodeType.AUTO.value()) {
			throw new AppException("0101",node.getName() + "不是人工处理节点，不能再分配！");
		}
		ActionResult r = this.ts.assign(task_id, emergency_level, priority_level, plan_start, plan_end, participant_id, null, null, null);
		if(node.getTaskStartType() == TaskStartType.AUTO.value()) {
			this.start(t.getTaskId(), participant_id);
		}
		return r;
	}
	
	//抢占任务
	@MethodInfo("抢占任务")
	public ActionResult seize(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.EMERGENCY_LEVEL, required=false, enumClass=EmergencyLevel.class,	desc="紧急程度") Integer emergency_level,
			@Param(value=Task.PRIORITY_LEVEL, required=false,enumClass=PriorityLevel.class,	desc="优先级别") Integer priority_level,
			@Param(value=Task.PLAN_START, required=false,	desc="计划开始时间") Date plan_start,
			@Param(value=Task.PLAN_END, required=false,	desc="计划结束时间") Date plan_end) throws Exception {
		Principal p = ServiceContext.getContext().getPrincipal();
		this.tps.findById(task_id, p.getName());
		return this.assign(task_id, emergency_level, priority_level, plan_start, plan_end, p.getName());
	}
	
	@MethodInfo("开始任务")
	public ActionResult start(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.PARTICIPANT_ID, required=true,	desc="执行人") String participant_id) throws Exception {
		Task t = this.ts.findById(task_id);
		if(t.getStatus() != TaskStatus.ASSIGNED.value()) {
			throw new AppException("0101","已分配状态任务才能开始");
		}
		if(!StringUtils.equals(participant_id, t.getParticipantId())) {
			throw new AppException("0101","已分配人员自己才能开始任务");
		}
		NodeInstance ni = this.nis.findById(t.getNodeInstanceId());
		if(ni.getActualStart() == null) {
			this.nis.start(ni.getNodeInstanceId(), null, null, null, null);
		}
		return this.ts.start(task_id, participant_id, null, null, null, null);
	}
	
	@MethodInfo("分解任务")
	@Transaction
	public Task split(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.NAME, required=true,	desc="名称") String name,
			@Param(value=Task.REMARKS, required=false,	desc="说明") String remarks,
			@Param(value=Task.SUB_BUSINESS_CODE, required=true,	desc="子业务代码") String subBusinessCode,
			@Param(value=Task.EMERGENCY_LEVEL, required=false, enumClass=EmergencyLevel.class,	desc="紧急程度") Integer emergency_level,
			@Param(value=Task.PRIORITY_LEVEL, required=false, enumClass=PriorityLevel.class, desc="优先级别") Integer priority_level,
			@Param(value=Task.ORG_CODE, required=false,	desc="机构代码") String orgCode,
			@Param(value=Task.PARTICIPANT_ID, required=true,	desc="执行人") String participant_id,
			@Param(value=Task.PLAN_START, required=false,	desc="计划开始时间") Date plan_start,
			@Param(value=Task.PLAN_END, required=false,	desc="计划结束时间") Date plan_end) throws Exception {
		Task t = this.ts.split(task_id, name, remarks, subBusinessCode, emergency_level, priority_level, orgCode, participant_id, plan_start, plan_end);
		if(!StringUtils.isEmpty(participant_id)) {
			this.assign(t.getTaskId(), emergency_level, priority_level, plan_start, plan_end, participant_id);
		}
		return t;
	}
	
	@MethodInfo("提交任务")
	@Transaction
	public ActionResult commit(
			@Param(value=Task.TASK_ID, required=true,	desc="任务ID") Long task_id,
			@Param(value=Task.DEAL_RESULT, required=true, enumClass=DealResult.class,	desc="处理结果") Integer dealResult,
			@Param(value=Task.CONTENT, required=true,	desc="提交内容") String content,
			@Param(value=Task.FORM_DATA, required=false,	desc="表单数据") Data form_data,
			@Param(value=Task.PARTICIPANT_ID, required=false,	desc="指定执行人") String[] participant_ids,
			@Param(value=NodeTransition.TO_NODE_ID,   required=false,   desc="后续节点" ) Long to_node_id) throws Exception {
		if(dealResult == DealResult.PROCESSING.value()) {
			throw new AppException("0101","处理结果不能是处理中");
		}
		Task t = this.ts.findById(task_id);
		if(t != null) {
			Node n = this.ns.findById(t.getNodeId());
			if(n.getRouteMode() == RouteMode.MANUAL.value()) {
				if(to_node_id == null) {
					throw new AppException("0101","必需指定后续环节ID");
				}
				this.nts.findByTowNodeId(n.getNodeId(), to_node_id);
			} else {
				to_node_id = null;
			}
		}
		Principal p = ServiceContext.getContext().getPrincipal();
		if(!StringUtils.equals(t.getParticipantId(), p.getName())) {
			throw new AppException("0101","任务不属于当前用户，不能处理！");
		}
		if(t.getStatus() != TaskStatus.PROCESSING.value()) {
			throw new AppException("0101","处理中状态的任务才能提交");
		}
//		Node curNode = this.ns.findById(t.getNodeId());
//		if(curNode.getNodeType() == NodeType.START.value() && dealResult == DealResult.SEND_BACK.value()) {
//			throw new AppException("0101","开始环节任务不能退回");
//		}
		//1、提交当前任务
		this.ts.commit(task_id, content, dealResult, form_data);
		ForwardContext context = new ForwardContext(task_id);
		//2、如果回退，优先回退
//		if(dealResult == DealResult.SEND_BACK.value()) {
//			return this.sendback(context.getFlowInstance());
//		}
		//2、检查节点完成情况
		List<Task> currentTasks= context.getBrotherTasks();
		if(FinishCondition.ALL.value().equals(context.getNode().getFinishCondition())) {	//全部完成并成功环节才成功，否则失败
			final long unfinishCount = currentTasks.stream().filter(x -> x.getStatus() != TaskStatus.FINISHED.value()).count();
			if(unfinishCount > 0) 
				return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS); //有未完成任务，直接结束，待继续处理
			
			final long failCount = currentTasks.stream().filter(x -> x.getDealResult() == DealResult.FAILED.value()).count();
			if(failCount > 0) 
				return this.finishFlowInstance(context, false);		//流程失败结束
		} else if(FinishCondition.MULTITUDE.value().equals(context.getNode().getFinishCondition())) { //多数完成
			final long successCount = currentTasks.stream().filter(x -> (x.getDealResult() == DealResult.SUCCESS.value())).count();
			final long failCount = currentTasks.stream().filter(x -> x.getDealResult() == DealResult.FAILED.value()).count();
			final long processingCount = currentTasks.stream().filter(x -> x.getDealResult() == DealResult.PROCESSING.value()).count();
			
			if(failCount > currentTasks.size() / 2) 
				return this.finishFlowInstance(context, false);		//失败占多数，流程失败结束
			if(successCount < currentTasks.size() / 2 && processingCount > 0) //等待继续处理未完成的任务
				return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		} else {
			final long successCount = currentTasks.stream().filter(x -> (x.getDealResult() == DealResult.SUCCESS.value())).count();
			final long failCount = currentTasks.stream().filter(x -> x.getDealResult() == DealResult.FAILED.value()).count();
			final long processingCount = currentTasks.stream().filter(x -> x.getDealResult() == DealResult.PROCESSING.value()).count();
			
			if(failCount == currentTasks.size())  //所有都失败了，失败结束
				return this.finishFlowInstance(context, false);		//失败占多数，流程失败结束
			if(successCount == 0 && processingCount > 0) //等待继续处理未完成的任务
				return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		}
		//3、环节成功离开
		this.fireLeaveNodeEvent(context);
		//4、流程结束判断
		List<Node> nextNodes = context.getNextNodes(to_node_id);
		if(nextNodes.size() == 1 && nextNodes.get(0).getNodeType() == NodeType.END.value()) { //流程成功结束
			List<NodeInstance> nodeInstances = nis.findByFlowInstanceId(context.getTask().getFlowInstanceId(), 
					new Integer[] {NodeInstanceStatus.CREATED.value(), NodeInstanceStatus.PROCESSING.value()}).getRows();
			if(nodeInstances.isEmpty()) {  //没有并行分支，流程结束
				return this.finishFlowInstance(context, true);
			} else {    //有并行分支，本环节处理结束
				return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
			}
		}
		//4、搜索后续环节，创建环节任务
		List<String> curParticipants = Utils.newArrayList();
		currentTasks.stream().forEach(x -> curParticipants.add(x.getParticipantId()));
		int nextTaskCount = this.createNextTasks(context.getFlowInstance(), nextNodes, participant_ids, currentTasks, context.getOrgCode(), 
				context.getBusinessType(), context.getBusinessCode());
		if(nextTaskCount == 0) { //无法创建后续环节任务，流程异常结束
			return this.finishFlowInstance(context, false);
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	//
	private int createNextTasks(FlowInstance fi, List<Node> nextNodes, String[] participant_ids, List<Task> currentTasks, String orgCode, 
			String businessType, String businessCode) throws Exception {
		int nextTaskCount = 0;
		for(Node node: nextNodes) {
			if(node.getNodeType() == NodeType.END.value()) continue;
			//
			NodeInstance ni = this.nis.createByNode("SYSTEM", fi.getFlowInstanceId(), node);
			
			List<Participant> participants = null;
			if(participant_ids != null && participant_ids.length > 0) {     //指定Participant
				participants = this.ps.findById(participant_ids);
			} else {
				final String participantType = node.getNodeType() == NodeType.AUTO.value()?"SYSTEM": node.getParticipantType(); //NodeType判断
				participants = ppf.
						getParticipantProvider(participantType).
						getParticipants(orgCode, businessType, node.getParticipantsSource());
			}
			
			if(node.getTaskMode() == TaskMode.ONE_TASK_ONE_PARTICIPANT.value()) {  //为每一个参与者分配一个任务，任务创建后即为已分配状态
				for(Participant p: participants) {
					Task nt = this.ts.createByNodeInstance(orgCode, businessType, businessCode, p.getId(), ni);
					this.assign(nt.getTaskId(), null, null, null, null, p.getId());
					nextTaskCount ++;
				}
			} else if(node.getTaskMode() == TaskMode.ONE_TASK_ONE_OF_PARTICIPANTS.value()) {  //创建一个待分配的任务，分配人员为资源池中的人员
				Task nt = this.ts.createByNodeInstance(orgCode, businessType, businessCode, null, ni);
				for(Participant p: participants) {
					this.tps.create(nt.getTaskId(), p.getId(), p.getName());
				}
				if(participants.size() == 1) {
					this.assign(nt.getTaskId(), null, null, null, null, participants.get(0).getId());
				}
				nextTaskCount ++;
			} else if(node.getTaskMode() == TaskMode.SEIZE.value()) {  //为当前环节创建一个任务，所有参与者抢占任务
				Task nt = this.ts.createByNodeInstance(orgCode, businessType, businessCode, null, ni);
				for(Participant p: participants) {
					this.tps.create(nt.getTaskId(), p.getId(), p.getName());
				}
				if(participants.size() == 1) {
					this.assign(nt.getTaskId(), null, null, null, null, participants.get(0).getId());
				}
				nextTaskCount ++;
			} else if(node.getTaskMode() == TaskMode.INHERIT_FROM_PRE_NODE.value()) {  //继承上一个环节的任务数量和处理人
				List<NodeInstance> listni = this.nis.findByFlowInstanceIdAndNodeId(ni.getFlowInstanceId(), ni.getNodeId()).getRows();
				if(listni.size() > 1) { //如果时回退节点实例，集成上一次的操作
					NodeInstance ni2 = listni.get(1);
					final List<Task> tasks2 = this.ts.findByNodeInstanceId(ni2.getNodeInstanceId(), new Integer[] {TaskStatus.FINISHED.value()}).getRows();
					for(Task ot: tasks2) {
						if(ot.getParentId() != null || ot.getParticipantId() == null || 
								ot.getStatus() != TaskStatus.FINISHED.value() || ot.getDealResult() != DealResult.SUCCESS.value()) continue;
						Task nt = this.ts.createByNodeInstance(orgCode, businessType, businessCode, null, ni2);
						this.assign(nt.getTaskId(), null, null, null, null, ot.getParticipantId());
						nextTaskCount ++;
					}
				} else {   //否则，取上一环节的任务
					if(currentTasks != null) {
						for(Task ot: currentTasks) {
							if(ot.getParentId() != null || ot.getParticipantId() == null || 
									ot.getStatus() != TaskStatus.FINISHED.value() || ot.getDealResult() != DealResult.SUCCESS.value()) continue;
							Task nt = this.ts.createByNodeInstance(orgCode, businessType, businessCode, null, ni);
							this.assign(nt.getTaskId(), null, null, null, null, ot.getParticipantId());
							nextTaskCount ++;
						}
					}
				}
			}
		}
		return nextTaskCount;
	}
	
	private void fireEnterNodeEvent(Node n, NodeInstance ni, String event) {
		if(StringUtils.isEmpty(n.getEventOnEnter())) return;
	}
	
	private void fireLeaveNodeEvent(ForwardContext context) throws Exception {
		//更新流程实例状态
		if(context.getNode().getNodeType() == NodeType.START.value()) { 
			this.fis.start(context.getTask().getFlowInstanceId());
		}
		//更新流程节点实例状态
		List<Task> tasks= context.getBrotherTasks();
		final String successParticipants = tasks.stream().
				filter(x -> (x.getStatus() == TaskStatus.FINISHED.value() && x.getDealResult() == DealResult.SUCCESS.value())).
				map(x -> x.getParticipantId()).collect(Collectors.joining(";"));
		Long consume_seconds = tasks.stream().mapToLong(x -> (x.getActualEnd().getTime() - x.getActualStart().getTime())).sum() / 1000;
		this.nis.finish(context.getNodeInstance().getNodeInstanceId(), NodeInstanceStatus.SUCCESS.value(), successParticipants, null, null, consume_seconds, null, null);
		//取消未完成任务
		tasks.stream().
				filter(x -> (x.getStatus() != TaskStatus.FINISHED.value() && x.getStatus() != TaskStatus.CANCELED.value())).
				forEach(x -> ts.cancel(x.getTaskId(), null, null, null));
		//
		Map<String, Object> env = Utils.newHashMap();
		env.put(NodeInstance.NODE_INSTANCE_ID, context.getTask().getNodeInstanceId());
		env.put(Task.TASK_ID, context.getTask().getTaskId());
		FlowUtils.process(context.getNode().getEventOnLeave(), context.getNode().getFormData(), env);
	}
	
	private ActionResult finishFlowInstance(ForwardContext context, boolean successFlat) throws Exception {
		//取消所有未完成的节点实例和任务
		List<NodeInstance> nodeInsts = this.nis.findByFlowInstanceId(context.getTask().getFlowInstanceId(), 
				new Integer[]{NodeInstanceStatus.CREATED.value(), NodeInstanceStatus.PROCESSING.value()}).getRows();
		nodeInsts.stream().forEach(ni -> {
			List<Task> lt = this.ts.findByNodeInstanceId(ni.getNodeInstanceId(), 
					new Integer[] {TaskStatus.ASSIGNING.value(), TaskStatus.ASSIGNED.value(), TaskStatus.PROCESSING.value()}).getRows();
			lt.stream().forEach(x -> {
				this.ts.cancel(x.getTaskId(), TaskStatus.CANCELED.value(), null, null);
			});
		});
		//更新流程实例状态
		this.fis.finish(context.getTask().getFlowInstanceId(), successFlat?FlowInstanceStatus.SUCCESS.value(): FlowInstanceStatus.FAIL.value());
		//触发流程结束事件
		Map<String, Object> env = Utils.newHashMap();
		env.put(FlowInstance.FLOW_INSTANCE_ID, context.getTask().getFlowInstanceId());
		env.put(Task.TASK_ID, context.getTask().getTaskId());
		FlowUtils.process(context.getEndNode().getEventOnLeave(), context.getNode().getFormData(), env);
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
//	private ActionResult sendback(FlowInstance flowInstance) throws Exception { //TODO 回退
//		return null;
//	}
	
	private boolean canPass(FlowInstance flowInstance, Node fromNode, Node toNode, String condition) {
		return true;
	}
	
	private List<Node> findNextNodes(FlowInstance fi, Node fromNode, Long to_node_id) throws Exception {
		List<Node> nextNodes = Utils.newArrayList();
		if(to_node_id != null) {
			nextNodes.add(ns.findById(to_node_id));
			return nextNodes;
		} else {
			List<Node> all = ns.findNextNodes(fromNode.getNodeId()).getRows();
			all.stream().forEach(x -> {
				NodeTransition nt = nts.findByTowNodeId(fromNode.getNodeId(), x.getNodeId());
				if(canPass(fi, fromNode, x, nt.getCondition())) {
					nextNodes.add(x);
				}
			});
		}
		return nextNodes;
	}
	
	public class ForwardContext {
		private final Task task;
		
		private FlowDefinition flowDefinition;
		private Node node;
		
		private FlowInstance flowInstance; 
		
		private NodeInstance nodeInstance;
		
		private List<Task> brotherTasks;
		
		ForwardContext(Long task_id) throws Exception {
			this.task = ts.findById(task_id);
		}
		
		public Task getTask() {
			return this.task;
		}
		
		public List<Task> getBrotherTasks() throws Exception {
			if(brotherTasks == null) {
				brotherTasks = ts.findByNodeInstanceId(task.getNodeInstanceId(), null).getRows();
			}
			return brotherTasks;
		}
		
		public FlowDefinition getFlowDefinition() throws Exception {
			if(this.flowDefinition == null) {
				this.flowDefinition = fds.findById(task.getFlowDefId());
			}
			return flowDefinition;
		}
		
		public Node getNode() throws Exception {
			if(this.node == null) {
				this.node = ns.findById(task.getNodeId());
			}
			return this.node;
		}
		
		public FlowInstance getFlowInstance() throws Exception {
			if(this.flowInstance == null) {
				this.flowInstance = fis.findById(task.getFlowInstanceId());
			}
			return flowInstance;
		}
		
		public NodeInstance getNodeInstance() throws Exception {
			if(this.nodeInstance == null) {
				this.nodeInstance = nis.findById(task.getNodeInstanceId());
			}
			return nodeInstance;
		}
		
		public String getOrgCode() throws Exception {
			return this.getFlowInstance().getOrgCode();
		}
		
		public String getBusinessType() throws Exception {
			return this.getFlowInstance().getBusinessType();
		}
		
		public String getBusinessCode() throws Exception {
			return this.getFlowInstance().getBusinessCode();
		}
		//
		public List<Node> getNextNodes(Long to_node_id) throws Exception {
			return findNextNodes(this.getFlowInstance(), this.getNode(), to_node_id);
		}
		
		public Node getEndNode() throws Exception {
			return this.getFlowDefinition().getNodes().stream().
					filter(x -> x.getNodeType() == NodeType.END.value()).findFirst().get();
		}
	}
}
